package com.xinqi.modules.sys.stats.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.xinqi.common.base.error.CommonErrors;
import com.xinqi.common.base.response.PageInfo;
import com.xinqi.common.base.response.Param;
import com.xinqi.common.core.enums.CourseContentTypeEnum;
import com.xinqi.common.core.enums.RegisterRoleEnum;
import com.xinqi.common.mybatis.utils.Pages;
import com.xinqi.common.mybatis.utils.Queries;
import com.xinqi.modules.sys.stats.convert.SchoolTeachingDayStConvert;
import com.xinqi.modules.sys.stats.domain.SchoolTeachingDayStEntity;
import com.xinqi.modules.sys.stats.dto.req.SchoolTeachingDayStCreateDTO;
import com.xinqi.modules.sys.stats.dto.req.SchoolTeachingDayStDeleteDTO;
import com.xinqi.modules.sys.stats.dto.req.SchoolTeachingDayStQueryDTO;
import com.xinqi.modules.sys.stats.dto.req.SchoolTeachingDayStUpdateDTO;
import com.xinqi.modules.sys.stats.dto.rsp.SchoolOverviewResultDTO;
import com.xinqi.modules.sys.stats.dto.rsp.SchoolTeachingCountResultDTO;
import com.xinqi.modules.sys.stats.dto.rsp.SchoolTeachingDayStResultDTO;
import com.xinqi.modules.sys.stats.manager.SchoolTeachingDayStManager;
import com.xinqi.modules.sys.stats.service.ClassTeachingDayStService;
import com.xinqi.modules.sys.stats.service.SchoolTeachingDayStService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.validation.Valid;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 学校教学数据统计服务层实现
 *
 * @author: wenjie
 * @date: 2023/08/24
 */
@Validated
@Service("schoolTeachingDayStService")
@RequiredArgsConstructor
public class SchoolTeachingDayStServiceImpl implements SchoolTeachingDayStService {
    private final SchoolTeachingDayStConvert schoolTeachingDayStConvert;
    private final SchoolTeachingDayStManager schoolTeachingDayStManager;
    private final ClassTeachingDayStService classTeachingDayStService;
    private final IdentifierGenerator defaultIdentifierGenerator;

    /**
     * 分页查询 学校教学数据统计数据
     */
    @Override
    public PageInfo<SchoolTeachingDayStResultDTO> page(Param pageable, SchoolTeachingDayStQueryDTO query) {
        LocalDateTime startTime = query.getStatDate().atTime(LocalTime.MIN);
        LocalDateTime endTime = query.getEndDate().atTime(LocalTime.MAX);
        IPage<SchoolTeachingDayStResultDTO> page = schoolTeachingDayStManager.findPage(Pages.page(pageable), startTime, endTime);
        this.setActivitySort(page.getRecords(), query);
        return Pages.convert(page);
    }

    /**
     * 设置今日排名和昨日排名
     *
     * @param records
     * @param query
     */
    private void setActivitySort(List<SchoolTeachingDayStResultDTO> records, SchoolTeachingDayStQueryDTO query) {
        if (CollectionUtils.isNotEmpty(records)) {
            List<SchoolTeachingCountResultDTO> todaySortList = schoolTeachingDayStManager.findContentSortListAndDateRange(query.getStatDate().atTime(LocalTime.MIN), query.getEndDate().atTime(LocalTime.MAX));

            LocalDateTime prevStartTime = query.getStatDate().minusDays(ChronoUnit.DAYS.between(query.getStatDate(), query.getEndDate()) + 1).atTime(LocalTime.MIN);
            LocalDateTime prevEndTime = query.getStatDate().minusDays(1).atTime(LocalTime.MAX);
            List<SchoolTeachingCountResultDTO> yesterdaySortList = schoolTeachingDayStManager.findContentSortListAndDateRange(prevStartTime, prevEndTime);

            Map<String, Integer> todaySortedMap = IntStream.range(0, todaySortList.size())
                .boxed()
                .collect(Collectors.toMap(i -> todaySortList.get(i).getSchoolName(), i -> i + 1));

            Map<String, Integer> yesterdaySortedMap = IntStream.range(0, yesterdaySortList.size())
                .boxed()
                .collect(Collectors.toMap(i -> yesterdaySortList.get(i).getSchoolName(), i -> i + 1));

            if (CollectionUtils.isEmpty(yesterdaySortList)) {
                for (SchoolTeachingDayStResultDTO record : records) {
                    record.setTodayActivitySort(todaySortedMap.get(record.getSchoolName()));
                    record.setYesterdayActivitySort(todaySortedMap.size() + 1);
                }
            } else {
                for (SchoolTeachingDayStResultDTO record : records) {
                    record.setTodayActivitySort(todaySortedMap.get(record.getSchoolName()));
                    if (yesterdaySortedMap.containsKey(record.getSchoolName())) {
                        record.setYesterdayActivitySort(yesterdaySortedMap.get(record.getSchoolName()));
                    } else {
                        record.setYesterdayActivitySort(todaySortedMap.size() + 1);
                    }
                }
            }
        }
    }

    /**
     * 查询 学校教学数据统计数据
     */
    @Override
    public List<SchoolTeachingDayStResultDTO> find(SchoolTeachingDayStQueryDTO query) {
        return schoolTeachingDayStManager.list(wrapper(query)).stream().map(schoolTeachingDayStConvert::convert).collect(Collectors.toList());
    }

    private LambdaQueryWrapper<SchoolTeachingDayStEntity> wrapper(SchoolTeachingDayStQueryDTO query) {
        LambdaQueryWrapper<SchoolTeachingDayStEntity> wrapper = Queries.lambda();
        Queries.accept(query.getId(), v -> wrapper.eq(SchoolTeachingDayStEntity::getId, v));
        return wrapper;
    }

    /**
     * 根据id查询 学校教学数据统计数据
     */
    @Override
    public SchoolTeachingDayStResultDTO findById(Long id) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        return Optional.ofNullable(schoolTeachingDayStManager.findById(id)).map(schoolTeachingDayStConvert::convert).orElse(null);
    }

    /**
     * 新增 学校教学数据统计数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long insert(@Valid SchoolTeachingDayStCreateDTO dto) {
        SchoolTeachingDayStEntity entity = schoolTeachingDayStConvert.create(dto);
        schoolTeachingDayStManager.save(entity);
        return entity.getId();
    }

    /**
     * 根据id修改 学校教学数据统计数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateById(@Valid SchoolTeachingDayStUpdateDTO dto) {
        Optional.ofNullable(schoolTeachingDayStManager.getById(dto.getId())).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("数据不存在"));
        SchoolTeachingDayStEntity entity = schoolTeachingDayStConvert.update(dto);
        return schoolTeachingDayStManager.updateById(entity);
    }

    /**
     * 根据id删除 学校教学数据统计数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(Long id) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        return schoolTeachingDayStManager.removeById(id);
    }

    /**
     * 根据id删除 学校教学数据统计数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(SchoolTeachingDayStDeleteDTO dto) {
        return deleteById(dto.getId());
    }

    /**
     * 更新逻辑：先更新学校，保存之后再更新其他的，后面的要用到前面保存到的班级，所以这里updateToday新增学校和更新其他属性的逻辑不能放在一个事务里
     * 由于是定时任务，所以偶尔的事务失败不用关心，容容错率较高
     *
     * @return Boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateToday() {
        this.updateSchoolTeaching();
        classTeachingDayStService.updateToday();
        return true;
    }

    @Override
    public SchoolOverviewResultDTO overview() {
        return schoolTeachingDayStManager.overview();
    }

    /**
     * 为每个学校设置值并保存数量， 可以采取两种策略，可以更新学校数据的时候级联更新班级数据，也可以分开更新，这里为了测试方便，分开更新
     */

    @Transactional(rollbackFor = Exception.class)
    public void updateSchoolTeaching() {

        LocalDate today = LocalDate.now();
        LocalDateTime todayStartTime = today.atTime(LocalTime.MIN);
        LocalDateTime todayEndTime = today.atTime(LocalTime.MAX);

        //从用户表中获取所有的学校，但返回成  SchoolTeachingDayStEntity 实体
        List<SchoolTeachingDayStEntity> schoolEntityList = schoolTeachingDayStManager.findAllSchools();

        //今日截止目前新增学校数量
        Map<String, Integer> todayAddedSchoolCount = schoolTeachingDayStManager.findTodayAddedSchool().stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));

        //老师总数
        Map<String, Integer> totalTeacherCount = schoolTeachingDayStManager.findCountByRole(RegisterRoleEnum.TEACHER.getCode()).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));
        //今日截止目前新增老师
        Map<String, Integer> todayTeacherAddedCount = schoolTeachingDayStManager.findAddedCountByRoleAndDateRange(RegisterRoleEnum.TEACHER.getCode(), todayStartTime, todayEndTime).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));
        //今日截止目前老师活跃数
        Map<String, Integer> todayTeacherActivityCount = schoolTeachingDayStManager.findActivityCountByRoleAndDateRange(RegisterRoleEnum.TEACHER.getCode(), todayStartTime, todayEndTime).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));

        //学生总数
        Map<String, Integer> totalStudentCount = schoolTeachingDayStManager.findCountByRole(RegisterRoleEnum.STUDENT.getCode()).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));
        //今日截止目前新增学生
        Map<String, Integer> todayStudentAddedCount = schoolTeachingDayStManager.findAddedCountByRoleAndDateRange(RegisterRoleEnum.STUDENT.getCode(), todayStartTime, todayEndTime).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));
        //今日截止目前学生活跃数
        Map<String, Integer> todayStudentActivityCount = schoolTeachingDayStManager.findActivityCountByRoleAndDateRange(RegisterRoleEnum.STUDENT.getCode(), todayStartTime, todayEndTime).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));

        //家长总数
        Map<String, Integer> totalPatriarchCount = schoolTeachingDayStManager.findCountByRole(RegisterRoleEnum.PARENT.getCode()).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));
        //今日截止目前新增家长
        Map<String, Integer> todayPatriarchAddedCount = schoolTeachingDayStManager.findAddedCountByRoleAndDateRange(RegisterRoleEnum.PARENT.getCode(), todayStartTime, todayEndTime).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));
        //今日截止目前家长活跃数
        Map<String, Integer> todayPatriarchActivityCount = schoolTeachingDayStManager.findActivityCountByRoleAndDateRange(RegisterRoleEnum.PARENT.getCode(), todayStartTime, todayEndTime).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));

        //班级数量
        Map<String, Integer> courseClassCount = schoolTeachingDayStManager.findCourseClassCount().stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));

        //发布作业总数
        Map<String, Integer> totalHomeworkCount = schoolTeachingDayStManager.findCourseContentCountByType(CourseContentTypeEnum.HOMEWORK.getCode()).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));
        //今日截止目前作业统计
        Map<String, Integer> todayHomeworkCount = schoolTeachingDayStManager.findReleaseCourseContentByTypeAndDateRange(CourseContentTypeEnum.HOMEWORK.getCode(), todayStartTime, todayEndTime).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));

        //发布测评总数
        Map<String, Integer> totalExamCount = schoolTeachingDayStManager.findCourseContentCountByType(CourseContentTypeEnum.EXAM.getCode()).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));
        //今日截止目前测评统计
        Map<String, Integer> todayExamCount = schoolTeachingDayStManager.findReleaseCourseContentByTypeAndDateRange(CourseContentTypeEnum.EXAM.getCode(), todayStartTime, todayEndTime).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));

        //发布讨论总数
        Map<String, Integer> totalDiscussCount = schoolTeachingDayStManager.findCourseContentCountByType(CourseContentTypeEnum.DISCUSS.getCode()).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));
        //今日截止目前讨论统计
        Map<String, Integer> todayDiscussCount = schoolTeachingDayStManager.findReleaseCourseContentByTypeAndDateRange(CourseContentTypeEnum.DISCUSS.getCode(), todayStartTime, todayEndTime).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));

        //发布公告总数
        Map<String, Integer> totalNoticeCount = schoolTeachingDayStManager.findCourseContentCountByType(CourseContentTypeEnum.NOTICE.getCode()).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));
        //今日截止目前公告统计
        Map<String, Integer> todayNoticeCount = schoolTeachingDayStManager.findReleaseCourseContentByTypeAndDateRange(CourseContentTypeEnum.NOTICE.getCode(), todayStartTime, todayEndTime).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));

        //发布资料总数
        Map<String, Integer> totalResourceCount = schoolTeachingDayStManager.findCourseContentCountByType(CourseContentTypeEnum.DATA.getCode()).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));
        //今日截止目前资料统计
        Map<String, Integer> todayResourceCount = schoolTeachingDayStManager.findReleaseCourseContentByTypeAndDateRange(CourseContentTypeEnum.DATA.getCode(), todayStartTime, todayEndTime).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));

        //发布课件总数
        Map<String, Integer> totalCoursewareCount = schoolTeachingDayStManager.findCourseContentCountByType(CourseContentTypeEnum.COURSEWARE.getCode()).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));
        //今日截止目前课件统计
        Map<String, Integer> todayCoursewareCount = schoolTeachingDayStManager.findReleaseCourseContentByTypeAndDateRange(CourseContentTypeEnum.COURSEWARE.getCode(), todayStartTime, todayEndTime).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));

        //发布视频总数
        Map<String, Integer> totalVideoCount = schoolTeachingDayStManager.findCourseContentCountByType(CourseContentTypeEnum.INTERACTIVE_VIDEO.getCode()).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));
        //今日截止目前视频统计
        Map<String, Integer> todayVideoCount = schoolTeachingDayStManager.findReleaseCourseContentByTypeAndDateRange(CourseContentTypeEnum.INTERACTIVE_VIDEO.getCode(), todayStartTime, todayEndTime).stream().collect(Collectors.toMap(SchoolTeachingCountResultDTO::getSchoolName, SchoolTeachingCountResultDTO::getCounts, (v1, v2) -> v2));

        //设置数量
        schoolEntityList.forEach(school -> {
            school.setSchoolId(defaultIdentifierGenerator.nextId(null).longValue());
            school.setStatsDate(today);

            if (todayAddedSchoolCount.containsKey(school.getSchoolName())) {
                school.setTodaySchool(todayAddedSchoolCount.get(school.getSchoolName()));
            }

            if (totalTeacherCount.containsKey(school.getSchoolName())) {
                school.setTotalTeacher(totalTeacherCount.get(school.getSchoolName()));
            }

            if (todayTeacherAddedCount.containsKey(school.getSchoolName())) {
                school.setTodayTeacher(todayTeacherAddedCount.get(school.getSchoolName()));
            }

            if (todayTeacherActivityCount.containsKey(school.getSchoolName())) {
                school.setTodayActivityTeacher(todayTeacherActivityCount.get(school.getSchoolName()));
            }

            if (totalStudentCount.containsKey(school.getSchoolName())) {
                school.setTotalStudent(totalStudentCount.get(school.getSchoolName()));
            }

            if (todayStudentAddedCount.containsKey(school.getSchoolName())) {
                school.setTodayStudent(todayStudentAddedCount.get(school.getSchoolName()));
            }

            if (todayStudentActivityCount.containsKey(school.getSchoolName())) {
                school.setTodayActivityStudent(todayStudentActivityCount.get(school.getSchoolName()));
            }

            if (totalPatriarchCount.containsKey(school.getSchoolName())) {
                school.setTotalPatriarch(totalPatriarchCount.get(school.getSchoolName()));
            }

            if (todayPatriarchAddedCount.containsKey(school.getSchoolName())) {
                school.setTodayPatriarch(todayPatriarchAddedCount.get(school.getSchoolName()));
            }

            if (todayPatriarchActivityCount.containsKey(school.getSchoolName())) {
                school.setTodayActivityPatriarch(todayPatriarchActivityCount.get(school.getSchoolName()));
            }

            if (courseClassCount.containsKey(school.getSchoolName())) {
                school.setClassCount(courseClassCount.get(school.getSchoolName()));
            }

            if (totalHomeworkCount.containsKey(school.getSchoolName())) {
                school.setTotalHomework(totalHomeworkCount.get(school.getSchoolName()));
            }

            if (todayHomeworkCount.containsKey(school.getSchoolName())) {
                school.setTodayHomework(todayHomeworkCount.get(school.getSchoolName()));
            }

            if (totalExamCount.containsKey(school.getSchoolName())) {
                school.setTotalExam(totalExamCount.get(school.getSchoolName()));
            }

            if (todayExamCount.containsKey(school.getSchoolName())) {
                school.setTodayExam(todayExamCount.get(school.getSchoolName()));
            }

            if (totalDiscussCount.containsKey(school.getSchoolName())) {
                school.setTotalDiscuss(totalDiscussCount.get(school.getSchoolName()));
            }

            if (todayDiscussCount.containsKey(school.getSchoolName())) {
                school.setTodayDiscuss(todayDiscussCount.get(school.getSchoolName()));
            }

            if (totalNoticeCount.containsKey(school.getSchoolName())) {
                school.setTotalNotice(totalNoticeCount.get(school.getSchoolName()));
            }

            if (todayNoticeCount.containsKey(school.getSchoolName())) {
                school.setTodayNotice(todayNoticeCount.get(school.getSchoolName()));
            }

            if (totalResourceCount.containsKey(school.getSchoolName())) {
                school.setTotalResource(totalResourceCount.get(school.getSchoolName()));
            }

            if (todayResourceCount.containsKey(school.getSchoolName())) {
                school.setTodayResource(todayResourceCount.get(school.getSchoolName()));
            }

            if (totalCoursewareCount.containsKey(school.getSchoolName())) {
                school.setTotalCourseware(totalCoursewareCount.get(school.getSchoolName()));
            }

            if (todayCoursewareCount.containsKey(school.getSchoolName())) {
                school.setTodayCourseware(todayCoursewareCount.get(school.getSchoolName()));
            }

            if (totalVideoCount.containsKey(school.getSchoolName())) {
                school.setTotalVideo(totalVideoCount.get(school.getSchoolName()));
            }

            if (todayVideoCount.containsKey(school.getSchoolName())) {
                school.setTodayVideo(todayVideoCount.get(school.getSchoolName()));
            }

        });

        schoolTeachingDayStManager.saveBatch(schoolEntityList);
    }

}
